這天把「人類+AI」接進你已經成形的開發骨架:用一鍵化腳本把 AI 能做的事變成可靠重複的流程,用守門規則擋住 AI 產出的各種翻車現場。所有東西都要能在本地、CI、容器裡跑出同樣結果,這是前 28 天的共同語言。Hatch/Nox 一鍵化、鎖檔與可重現建置、型別與契約測、安全掃描與授權治理、觀測指標與追蹤都會被串起來。
AI 參與的開發活動大致分三類:
原則只有一條:把這些行為變成「可被腳本呼叫」的任務,塞進 Hatch/Nox 的單一入口,讓所有人用同一顆按鈕啟動它。
在 pyproject.toml 增加 AI 助攻的腳本群組,遵循我們既有的「一鍵多工」寫法【】:
[tool.hatch.envs.dev]
features = ["dev"]  # 你既有的開發 extra:pytest/ruff/black/mypy...
[tool.hatch.envs.dev.scripts]
check = [
  "ruff check .",
  "black --check .",
  "mypy src/",
  "pytest -q"
]
# --- AI 協作:全部「可選、可重演、可審計」 ---
ai:review = "python -m scripts.ai_review --diff HEAD"
ai:doc     = "python -m scripts.ai_doc    --paths src/"
ai:changelog = "python -m scripts.ai_changelog --since-tag $(git describe --tags --abbrev=0)"
noxfile.py 保持跨版本驗證,讓 AI 產物也走同一條 CI 流程路徑【】:
import nox
@nox.session(python=["3.10","3.11","3.12"])
def ai(session):
    session.install(".[dev]")  # 用你既有的 dev extra
    session.run("python","-m","scripts.ai_review","--sample")
重點:AI 腳本必須是純 Python/CLI,輸入固定、輸出可重演,避免「每次跑都長不一樣」的玄學。
延續 Day 10 的型別守門【】,再加三把刀:
# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      # 1) 禁止把 AI 產生的 Placeholder 混進 PR
      - id: forbid-ai-placeholders
        name: forbid ai placeholders
        entry: python scripts/gates/forbid_ai_placeholders.py
        language: system
        pass_filenames: true
      # 2) 文字/程式碼都要有規範格式
      - id: ruff
        name: ruff
        entry: ruff check .
        language: system
        pass_filenames: false
      - id: black
        name: black
        entry: black --check .
        language: system
        pass_filenames: false
      # 3) 型別守門(Day 10)
      - id: pyright
        name: pyright
        entry: pyright
        language: system
        pass_filenames: false
      - id: mypy
        name: mypy
        entry: mypy
        language: system
        pass_filenames: false
forbid_ai_placeholders.py 的檢查邏輯很無情也很有效:擋住 TODO/???/FIXME、「Generated by」「LLM」「示意用」等關鍵字與可疑檔頭,避免把未審核的自動產物直接合進主幹。
在的 hatch run ci 維持統一入口,於 CI job 增加「AI 結果一致性」與「安全治理」步驟,與掃描並排:
# ci.yml 片段
- name: AI review (idempotent)
  run: |
    uv run hatch run dev:ai:review --diff HEAD~1..HEAD --save .reports/ai_review.json
    test -s .reports/ai_review.json  # 有輸出才算過
- name: Security & license
  run: |
    uv run hatch run sec:check-all   # 延續 Day 26 的腳本組合
保持多階段建置,把「鎖檔」「非 root」「stdout JSON log」原則不變。AI 腳本若在 build 階段需要跑,務必使用 uv.lock 與 --frozen 同步依賴,避免不同鏡像生成不同結果。
這些腳本只做「提案」,不做「修改」。最後的決策仍由人類與傳統檢查來判定,與我們既有的型別/測試/安全守門一起工作,而不是互相取代。
scripts/ai_review.py:針對 diff 產生審查意見# 簡化版骨架:讀 diff -> 分段 -> 產 JSON 建議
# 真正的「模型調用」細節留白,確保可替換性(本地/雲端/代理皆可)
import json, subprocess, sys, re
def get_diff(spec="HEAD"):
    return subprocess.check_output(["git","diff",spec]).decode()
def review_hints(patch:str)->list[dict]:
    hints=[]
    if re.search(r"except Exception:", patch):
        hints.append({"severity":"warn","rule":"broad-except",
                      "msg":"避免捕太廣,改成具體例外並記錄 trace_id。"})
    if "print(" in patch:
        hints.append({"severity":"info","rule":"logging",
                      "msg":"用 structlog 打 JSON,攜帶 trace_id/span_id。【觀測 Day28】"})
    return hints
if __name__=="__main__":
    diff = get_diff(sys.argv[sys.argv.index("--diff")+1] if "--diff" in sys.argv else "HEAD")
    out = review_hints(diff)
    print(json.dumps(out,ensure_ascii=False,indent=2))
提示裡刻意提醒用 JSON log 並攜帶 trace_id/span_id,對應整合做法。
scripts/ai_doc.py:為模組與公開 API 產出 Docstring 草案src/ 公開介面docs/_drafts/*.md,不直接覆蓋原檔scripts/ai_changelog.py:生成變更日誌初稿CHANGELOG.md 草稿TODO/???/FIXME/Generated by 的變更合併trace_id/span_id,方便拉回整條追蹤hatch run 或 nox 觸發,不接受人肉命令差異hatch run dev:check,hatch run dev:ai:review
hatch run ci 跑完整檢查矩陣與 AI 建議輸出,附 artifact| 症狀 | 可能原因 | 修法 | 
|---|---|---|
| 每次 AI 輸出結果都不一樣 | 腳本內隨機種子或非鎖定環境 | 固定 random seed,依賴走 uv.lock --frozen,輸出成 JSON 並上傳 artifact 以比對 | 
| 本地過、CI 不過 | 入口不一致或依賴不同 | 一律 hatch run/nox,CI 先 uv sync --locked 再跑檢查 | 
| AI 建議忽略觀測欄位 | 程式碼沒帶追蹤上下文 | 先 setup OTel,再 setup logging,把 trace_id/span_id 注入 JSON log | 
Vibe coding 的重點不是「讓 AI 幫你寫」,而是「讓團隊在同一條可重演的軌道上寫」。把 AI 收編進你既有的工程化骨架:單一入口、一致環境、嚴格守門、全程可觀測。當流程站穩,AI 只是多了一個聰明又會犯錯的實習生,出手快,但每次都要留下證據和紀錄。這就是 Day 29 要你帶走的節奏。